#ifdef JA2_PRECOMPILED_HEADERS
	#include "JA2 SGP ALL.H"
#elif defined( WIZ8_PRECOMPILED_HEADERS )
	#include "WIZ8 SGP ALL.H"
#else
	#include "types.h"
	#include <windows.h>
	#include <stdio.h>
	#include <memory.h>
	#include "debug.h"
	#include "input.h"
	#include "memman.h"
	#include "english.h"
	#if defined( JA2 ) || defined( UTIL )
		#include "video.h"
	#else
		#include "video2.h"
	#endif
	#include "local.h"
#endif

#include "zmouse.h"

// Make sure to refer to the translation table which is within one of the following files (depending
// on the language used). ENGLISH.C, JAPANESE.C, FRENCH.C, GERMAN.C, SPANISH.C, etc...

extern UINT16 gsKeyTranslationTable[1024];

extern BOOLEAN gfApplicationActive;

#ifndef JA2

#undef GetCursorPos
#define GetCursorPos SGPMouseGetPos

#endif

// The gfKeyState table is used to track which of the keys is up or down at any one time. This is used while polling
// the interface.

BOOLEAN   gfKeyState[256];			// TRUE = Pressed, FALSE = Not Pressed
BOOLEAN   fCursorWasClipped = FALSE;
RECT      gCursorClipRect;



// The gsKeyTranslationTables basically translates scan codes to our own key value table. Please note that the table is 2 bytes
// wide per entry. This will be used since we will use 2 byte characters for translation purposes.

UINT16   gfShiftState;					// TRUE = Pressed, FALSE = Not Pressed
UINT16   gfAltState;						// TRUE = Pressed, FALSE = Not Pressed
UINT16   gfCtrlState;						// TRUE = Pressed, FALSE = Not Pressed

// These data structure are used to track the mouse while polling

BOOLEAN   gfTrackDblClick;
UINT32    guiDoubleClkDelay;		// Current delay in milliseconds for a delay
UINT32		guiSingleClickTimer;
UINT32		guiRecordedWParam;
UINT32		guiRecordedLParam;
UINT16		gusRecordedKeyState;
BOOLEAN		gfRecordedLeftButtonUp;

UINT32		guiLeftButtonRepeatTimer;
UINT32		guiRightButtonRepeatTimer;

BOOLEAN   gfTrackMousePos;			// TRUE = queue mouse movement events, FALSE = don't
BOOLEAN   gfLeftButtonState;		// TRUE = Pressed, FALSE = Not Pressed
BOOLEAN   gfRightButtonState;		// TRUE = Pressed, FALSE = Not Pressed
BOOLEAN   gfMiddleButtonState;		// TRUE = Pressed, FALSE = Not Pressed
int		gfWheelState = 0; // 1 = scroll up, 0 = idle, -1 = scroll down
UINT16    gusMouseXPos;					// X position of the mouse on screen
UINT16    gusMouseYPos;					// y position of the mouse on screen

// The queue structures are used to track input events using queued events

InputAtom gEventQueue[256];
UINT16    gusQueueCount;
UINT16    gusHeadIndex;
UINT16    gusTailIndex;

// ATE: Added to signal if we have had input this frame - cleared by the SGP main loop
BOOLEAN		gfSGPInputReceived = FALSE;

// This is the WIN95 hook specific data and defines used to handle the keyboard and
// mouse hook

HHOOK ghKeyboardHook;
HHOOK ghMouseHook;

// If the following pointer is non NULL then input characters are redirected to
// the related string

BOOLEAN      gfCurrentStringInputState;
StringInput *gpCurrentStringDescriptor;

// Local function headers

void    QueueEvent(UINT16 ubInputEvent, UINT32 usParam, UINT32 uiParam);
void    RedirectToString(UINT16 uiInputCharacter);
void		HandleSingleClicksAndButtonRepeats( void );
void		AdjustMouseForWindowOrigin(void);

// These are the hook functions for both keyboard and mouse

LRESULT CALLBACK KeyboardHandler(int Code, WPARAM wParam, LPARAM lParam)
{
#ifndef JA2
  if((Code < 0) || (!gfApplicationActive))
#else
  if (Code < 0)
#endif
  { // Do not handle this message, pass it on to another window
    return CallNextHookEx(ghKeyboardHook, Code, wParam, lParam);
  }

  if (lParam & TRANSITION_MASK)
  { // The key has been released
    KeyUp(wParam, lParam);
		//gfSGPInputReceived =  TRUE;
  }
  else
  { // Key was up
    KeyDown(wParam, lParam);
		gfSGPInputReceived =  TRUE;
  }

  return TRUE;
}

#ifdef JA2

LRESULT CALLBACK MouseHandler(int Code, WPARAM wParam, LPARAM lParam)
{
  UINT32 uiParam;

#ifndef JA2
  if((Code < 0) || (!gfApplicationActive))
#else
  if (Code < 0)
#endif
  { // Do not handle this message, pass it on to another window
    return CallNextHookEx(ghMouseHook, Code, wParam, lParam);
  }

  switch (wParam)
  {
    case WM_LBUTTONDOWN
    : // Update the current mouse position
      gusMouseXPos = (UINT16)(((MOUSEHOOKSTRUCT *)lParam)->pt).x;
      gusMouseYPos = (UINT16)(((MOUSEHOOKSTRUCT *)lParam)->pt).y;
      uiParam = gusMouseYPos;
      uiParam = uiParam << 16;
      uiParam = uiParam | gusMouseXPos;
      // Update the button state
			gfLeftButtonState = TRUE;
			//Set that we have input
      gfSGPInputReceived =  TRUE;
      // Trigger an input event
      QueueEvent(LEFT_BUTTON_DOWN, 0, uiParam);
	    break;
    case WM_LBUTTONUP
    : // Update the current mouse position
      gusMouseXPos = (UINT16)(((MOUSEHOOKSTRUCT *)lParam)->pt).x;
      gusMouseYPos = (UINT16)(((MOUSEHOOKSTRUCT *)lParam)->pt).y;
      uiParam = gusMouseYPos;
      uiParam = uiParam << 16;
      uiParam = uiParam | gusMouseXPos;
      // Update the button state
      gfLeftButtonState = FALSE;
			//Set that we have input
      gfSGPInputReceived =  TRUE;
      // Trigger an input event
      QueueEvent(LEFT_BUTTON_UP, 0, uiParam);
      break;
    case WM_RBUTTONDOWN
    : // Update the current mouse position
      gusMouseXPos = (UINT16)(((MOUSEHOOKSTRUCT *)lParam)->pt).x;
      gusMouseYPos = (UINT16)(((MOUSEHOOKSTRUCT *)lParam)->pt).y;
      uiParam = gusMouseYPos;
      uiParam = uiParam << 16;
      uiParam = uiParam | gusMouseXPos;
      // Update the button state
      gfRightButtonState = TRUE;
			//Set that we have input
      gfSGPInputReceived =  TRUE;
      // Trigger an input event
      QueueEvent(RIGHT_BUTTON_DOWN, 0, uiParam);
      break;
    case WM_RBUTTONUP
    : // Update the current mouse position
      gusMouseXPos = (UINT16)(((MOUSEHOOKSTRUCT *)lParam)->pt).x;
      gusMouseYPos = (UINT16)(((MOUSEHOOKSTRUCT *)lParam)->pt).y;
      uiParam = gusMouseYPos;
      uiParam = uiParam << 16;
      uiParam = uiParam | gusMouseXPos;
      // Update the button state
      gfRightButtonState = FALSE;
			//Set that we have input
      gfSGPInputReceived =  TRUE;
      // Trigger an input event
      QueueEvent(RIGHT_BUTTON_UP, 0, uiParam);
      break;
//<SB>
    case WM_MBUTTONDOWN
    : // Update the current mouse position
      gusMouseXPos = (UINT16)(((MOUSEHOOKSTRUCT *)lParam)->pt).x;
      gusMouseYPos = (UINT16)(((MOUSEHOOKSTRUCT *)lParam)->pt).y;
      uiParam = gusMouseYPos;
      uiParam = uiParam << 16;
      uiParam = uiParam | gusMouseXPos;
      // Update the button state
      gfMiddleButtonState = TRUE;
			//Set that we have input
      gfSGPInputReceived =  TRUE;
      // Trigger an input event
      QueueEvent(MIDDLE_BUTTON_DOWN, 0, uiParam);
      break;
    case WM_MBUTTONUP
    : // Update the current mouse position
      gusMouseXPos = (UINT16)(((MOUSEHOOKSTRUCT *)lParam)->pt).x;
      gusMouseYPos = (UINT16)(((MOUSEHOOKSTRUCT *)lParam)->pt).y;
      uiParam = gusMouseYPos;
      uiParam = uiParam << 16;
      uiParam = uiParam | gusMouseXPos;
      // Update the button state
      gfMiddleButtonState = FALSE;
			//Set that we have input
      gfSGPInputReceived =  TRUE;
      // Trigger an input event
      QueueEvent(MIDDLE_BUTTON_UP, 0, uiParam);
      break;
    case WM_MOUSEWHEEL
    : // Update the current mouse position
      gusMouseXPos = (UINT16)(((MOUSEHOOKSTRUCT *)lParam)->pt).x;
      gusMouseYPos = (UINT16)(((MOUSEHOOKSTRUCT *)lParam)->pt).y;
      uiParam = gusMouseYPos;
      uiParam = uiParam << 16;
      uiParam = uiParam | gusMouseXPos;
      // Update the button state
//		gfWheelState = *(((short*)&wParam)+1)/WHEEL_DELTA;
		//Set that we have input
      gfSGPInputReceived =  TRUE;
      // Trigger an input event
//      QueueEvent(MOUSE_WHEEL, 0, uiParam);
		return FALSE;
//	    break;
//</SB>
    case WM_MOUSEMOVE
    : // Update the current mouse position
      gusMouseXPos = (UINT16)(((MOUSEHOOKSTRUCT *)lParam)->pt).x;
      gusMouseYPos = (UINT16)(((MOUSEHOOKSTRUCT *)lParam)->pt).y;
      uiParam = gusMouseYPos;
      uiParam = uiParam << 16;
      uiParam = uiParam | gusMouseXPos;
      // Trigger an input event
      if (gfTrackMousePos == TRUE)
      {
        QueueEvent(MOUSE_POS, 0, uiParam);
      }
			//Set that we have input
      gfSGPInputReceived =  TRUE;
      break;
  }
  return TRUE;
}

#else

// Wizardry mouse hander

LRESULT CALLBACK MouseHandler(int Code, WPARAM wParam, LPARAM lParam)
{
UINT32 uiParam;
UINT32	uiXPos, uiYPos;
RECT rcClient;
BOOLEAN fOutsideClient=FALSE;
static BOOLEAN fResizing=FALSE;
LRESULT Result;

  uiXPos= (((MOUSEHOOKSTRUCT *)lParam)->pt).x;
  uiYPos=	(((MOUSEHOOKSTRUCT *)lParam)->pt).y;


	if(!VideoIsFullScreen())
	{
		if(wParam==WM_NCLBUTTONDOWN)
				fResizing=TRUE;

		VideoGetClientRect(&rcClient);
		if((uiXPos < (UINT32)rcClient.left) || (uiXPos > (UINT32)rcClient.right) || (uiYPos < (UINT32)rcClient.top) || (uiYPos > (UINT32)rcClient.bottom))
			fOutsideClient=TRUE;
	}

  if((Code < 0) || (!gfApplicationActive) || fOutsideClient || fResizing)
  { // Do not handle this message, pass it on to another window
    Result = CallNextHookEx(ghMouseHook, Code, wParam, lParam);

		if((wParam==WM_LBUTTONUP) || (wParam==WM_NCLBUTTONUP))
			fResizing=FALSE;

		return(Result);
  }

  switch (wParam)
  {
    case WM_LBUTTONUP:
    case WM_LBUTTONDOWN:
    case WM_RBUTTONDOWN:
    case WM_RBUTTONUP:
    case WM_MOUSEMOVE:
			if(VideoIsFullScreen())
			{
	      gusMouseXPos = (UINT16)(uiXPos);
		    gusMouseYPos = (UINT16)(uiYPos);
			}
			else
			{
		    gusMouseXPos = (UINT16)(uiXPos-rcClient.left);
	      gusMouseYPos = (UINT16)(uiYPos-rcClient.top);
			}
		  uiParam = (UINT32)gusMouseYPos<<16 | (UINT32)gusMouseXPos;
			//Set that we have input
      gfSGPInputReceived =  TRUE;
	    break;
  }
 
	if ( wParam == WM_MOUSEWHEEL )
	{
		return( FALSE );
	}
 
  switch (wParam)
  {
    case WM_LBUTTONDOWN:
			gfLeftButtonState = TRUE;
      QueueEvent(LEFT_BUTTON_DOWN, 0, uiParam);
	    break;
    case WM_LBUTTONUP:
			gfLeftButtonState = FALSE;
      QueueEvent(LEFT_BUTTON_UP, 0, uiParam);
      break;
    case WM_RBUTTONDOWN:
      gfRightButtonState = TRUE;
      QueueEvent(RIGHT_BUTTON_DOWN, 0, uiParam);
      break;
    case WM_RBUTTONUP:
      gfRightButtonState = FALSE;
      QueueEvent(RIGHT_BUTTON_UP, 0, uiParam);
      break;
    case WM_MOUSEMOVE:
      if(gfTrackMousePos)
        QueueEvent(MOUSE_POS, 0, uiParam);
      break;
  }

  return(TRUE);
}

#endif

BOOLEAN InitializeInputManager(void)
{
  // Link to debugger
  RegisterDebugTopic(TOPIC_INPUT, "Input Manager");
  // Initialize the gfKeyState table to FALSE everywhere
  memset(gfKeyState, FALSE, 256);
  // Initialize the Event Queue
  gusQueueCount = 0;
  gusHeadIndex  = 0;
  gusTailIndex  = 0;
  // By default, we will not queue mousemove events
  gfTrackMousePos = FALSE;
  // Initialize other variables
  gfShiftState = FALSE;
  gfAltState   = FALSE;
  gfCtrlState  = FALSE;
  // Initialize variables pertaining to DOUBLE CLIK stuff
  gfTrackDblClick = TRUE;
  guiDoubleClkDelay = DBL_CLK_TIME;
  guiSingleClickTimer = 0;
  gfRecordedLeftButtonUp = FALSE;
  // Initialize variables pertaining to the button states
  gfLeftButtonState  = FALSE;
  gfRightButtonState = FALSE;
  // Initialize variables pertaining to the repeat mechanism
  guiLeftButtonRepeatTimer = 0;
	guiRightButtonRepeatTimer = 0;
  // Set the mouse to the center of the screen
  gusMouseXPos = 320;
  gusMouseYPos = 240;
  // Initialize the string input mechanism
  gfCurrentStringInputState = FALSE;
  gpCurrentStringDescriptor = NULL;
  // Activate the hook functions for both keyboard and Mouse
  ghKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC) KeyboardHandler, (HINSTANCE) 0, GetCurrentThreadId());
  DbgMessage(TOPIC_INPUT, DBG_LEVEL_2, String("Set keyboard hook returned %d", ghKeyboardHook));

  ghMouseHook = SetWindowsHookEx(WH_MOUSE, (HOOKPROC) MouseHandler, (HINSTANCE) 0, GetCurrentThreadId());
  DbgMessage(TOPIC_INPUT, DBG_LEVEL_2, String("Set mouse hook returned %d", ghMouseHook));
  return TRUE;
}

void ShutdownInputManager(void)
{ // There's very little to do when shutting down the input manager. In the future, this is where the keyboard and
  // mouse hooks will be destroyed
  UnRegisterDebugTopic(TOPIC_INPUT, "Input Manager");
  UnhookWindowsHookEx(ghKeyboardHook);
  UnhookWindowsHookEx(ghMouseHook);
}

void QueuePureEvent(UINT16 ubInputEvent, UINT32 usParam, UINT32 uiParam)
{
  UINT32 uiTimer;
  UINT16 usKeyState;

  uiTimer = GetTickCount();
  usKeyState = gfShiftState | gfCtrlState | gfAltState;

  // Can we queue up one more event, if not, the event is lost forever
  if (gusQueueCount == 256)
  { // No more queue space
    return;
  }

  // Okey Dokey, we can queue up the event, so we do it
  gEventQueue[gusTailIndex].uiTimeStamp = uiTimer;
  gEventQueue[gusTailIndex].usKeyState = usKeyState;
  gEventQueue[gusTailIndex].usEvent = ubInputEvent;
  gEventQueue[gusTailIndex].usParam = usParam;
  gEventQueue[gusTailIndex].uiParam = uiParam;

  // Increment the number of items on the input queue
  gusQueueCount++;

  // Increment the gusTailIndex pointer
  if (gusTailIndex == 255)
  { // The gusTailIndex is about to wrap around the queue ring
    gusTailIndex = 0;
  }
  else
  { // We simply increment the gusTailIndex
    gusTailIndex++;
  }
}

void QueueEvent(UINT16 ubInputEvent, UINT32 usParam, UINT32 uiParam)
{
  UINT32 uiTimer;
  UINT16 usKeyState;

  uiTimer = GetTickCount();
  usKeyState = gfShiftState | gfCtrlState | gfAltState;

  // Can we queue up one more event, if not, the event is lost forever
  if (gusQueueCount == 256)
  { // No more queue space
    return;
  }

  if (ubInputEvent == LEFT_BUTTON_DOWN)
  {
    guiLeftButtonRepeatTimer = uiTimer + BUTTON_REPEAT_TIMEOUT;
  }

  if (ubInputEvent == RIGHT_BUTTON_DOWN)
  {
    guiRightButtonRepeatTimer = uiTimer + BUTTON_REPEAT_TIMEOUT;
  }

  if (ubInputEvent == LEFT_BUTTON_UP)
  {
    guiLeftButtonRepeatTimer = 0;
  }

  if (ubInputEvent == RIGHT_BUTTON_UP)
  {
    guiRightButtonRepeatTimer = 0;
  }

  if ( (ubInputEvent == LEFT_BUTTON_UP) )
  {
		// Do we have a double click
		if ( ( uiTimer - guiSingleClickTimer ) < DBL_CLK_TIME )
		{
				guiSingleClickTimer = 0;

				// Add a button up first...
				gEventQueue[gusTailIndex].uiTimeStamp = uiTimer;
				gEventQueue[gusTailIndex].usKeyState = gusRecordedKeyState;
				gEventQueue[gusTailIndex].usEvent = LEFT_BUTTON_UP;
				gEventQueue[gusTailIndex].usParam = usParam;
				gEventQueue[gusTailIndex].uiParam = uiParam;

				// Increment the number of items on the input queue
				gusQueueCount++;

				// Increment the gusTailIndex pointer
				if (gusTailIndex == 255)
				{ // The gusTailIndex is about to wrap around the queue ring
					gusTailIndex = 0;
				}
				else
				{ // We simply increment the gusTailIndex
					gusTailIndex++;
				}


				// Now do double click
				gEventQueue[gusTailIndex].uiTimeStamp = uiTimer;
				gEventQueue[gusTailIndex].usKeyState = gusRecordedKeyState ;
				gEventQueue[gusTailIndex].usEvent = LEFT_BUTTON_DBL_CLK;
				gEventQueue[gusTailIndex].usParam = usParam;
				gEventQueue[gusTailIndex].uiParam = uiParam;

				// Increment the number of items on the input queue
				gusQueueCount++;

				// Increment the gusTailIndex pointer
				if (gusTailIndex == 255)
				{ // The gusTailIndex is about to wrap around the queue ring
					gusTailIndex = 0;
				}
				else
				{ // We simply increment the gusTailIndex
					gusTailIndex++;
				}

				return;
		}
		else
		{
			// Save time
			guiSingleClickTimer = uiTimer;
		}
  }

  // Okey Dokey, we can queue up the event, so we do it
  gEventQueue[gusTailIndex].uiTimeStamp = uiTimer;
  gEventQueue[gusTailIndex].usKeyState = usKeyState;
  gEventQueue[gusTailIndex].usEvent = ubInputEvent;
  gEventQueue[gusTailIndex].usParam = usParam;
  gEventQueue[gusTailIndex].uiParam = uiParam;

  // Increment the number of items on the input queue
  gusQueueCount++;

  // Increment the gusTailIndex pointer
  if (gusTailIndex == 255)
  { // The gusTailIndex is about to wrap around the queue ring
    gusTailIndex = 0;
  }
  else
  { // We simply increment the gusTailIndex
    gusTailIndex++;
  }
}

BOOLEAN DequeueSpecificEvent(InputAtom *Event, UINT32 uiMaskFlags )
{
  // Is there an event to dequeue
  if (gusQueueCount > 0)
  {
		memcpy( Event, &( gEventQueue[gusHeadIndex] ), sizeof( InputAtom ) );

		// Check if it has the masks!
		if ( ( Event->usEvent & uiMaskFlags ) )
		{
			return( DequeueEvent( Event) );
		}
	}

	return( FALSE );
}

BOOLEAN DequeueEvent(InputAtom *Event)
{
	HandleSingleClicksAndButtonRepeats( );

  // Is there an event to dequeue
  if (gusQueueCount > 0)
  {
		// We have an event, so we dequeue it
		memcpy( Event, &( gEventQueue[gusHeadIndex] ), sizeof( InputAtom ) );

		if (gusHeadIndex == 255)
		{
			gusHeadIndex = 0;
		}
		else
		{
			gusHeadIndex++;
		}

		// Decrement the number of items on the input queue
		gusQueueCount--;

		// dequeued an event, return TRUE
		return TRUE;
  }
	else
	{
		// No events to dequeue, return FALSE
		return FALSE;
	}
}

void KeyChange(UINT32 usParam, UINT32 uiParam, UINT8 ufKeyState)
{
  UINT32 ubKey;
  UINT16 ubChar;
  POINT  MousePos;
  UINT32 uiTmpLParam;

  if ((usParam >= 96)&&(usParam <= 110))
  { // Well this could be a NUMPAD character imitating the center console characters (when NUMLOCK is OFF). Well we
    // gotta find out what was pressed and translate it to the actual physical key (i.e. if we think that HOME was
    // pressed but NUM_7 was pressed, the we translate the key into NUM_7
    switch(usParam)
    { case 96 // NUM_0
      : if (((uiParam & SCAN_CODE_MASK) >> 16) == 82)
        { // Well its the NUM_9 key and not actually the PGUP key
          ubKey = 223;
        }
        else
        { // NOP, its the PGUP key all right
          ubKey = usParam;
        }
        break;
      case 110 // NUM_PERIOD
      : if (((uiParam & SCAN_CODE_MASK) >> 16) == 83)
        { // Well its the NUM_3 key and not actually the PGDN key
          ubKey = 224;
        }
        else
        { // NOP, its the PGDN key all right
          ubKey = usParam;
        }
        break;
      case 97 // NUM_1
      : if (((uiParam & SCAN_CODE_MASK) >> 16) == 79)
        { // Well its the NUM_1 key and not actually the END key
          ubKey = 225;
        }
        else
        { // NOP, its the END key all right
          ubKey = usParam;
        }
        break;
      case 98 // NUM_2
      : if (((uiParam & SCAN_CODE_MASK) >> 16) == 80)
        { // Well its the NUM_7 key and not actually the HOME key
          ubKey = 226;
        }
        else
        { // NOP, its the HOME key all right
          ubKey = usParam;
        }
        break;
      case 99 // NUM_3
      : if (((uiParam & SCAN_CODE_MASK) >> 16) == 81)
        { // Well its the NUM_4 key and not actually the LARROW key
          ubKey = 227;
        }
        else
        { // NOP, it's the LARROW key all right
          ubKey = usParam;
        }
        break;
      case 100 // NUM_4
      : if (((uiParam & SCAN_CODE_MASK) >> 16) == 75)
        { // Well its the NUM_8 key and not actually the UPARROW key
          ubKey = 228;
        }
        else
        { // NOP, it's the UPARROW key all right
          ubKey = usParam;
        }
        break;
      case 101 // NUM_5
      : if (((uiParam & SCAN_CODE_MASK) >> 16) == 76)
        { // Well its the NUM_6 key and not actually the RARROW key
          ubKey = 229;
        }
        else
        { // NOP, it's the RARROW key all right
          ubKey = usParam;
        }
        break;
      case 102 // NUM_6
      : if (((uiParam & SCAN_CODE_MASK) >> 16) == 77)
        { // Well its the NUM_2 key and not actually the DNARROW key
          ubKey = 230;
        }
        else
        { // NOP, it's the DNARROW key all right
          ubKey = usParam;
        }
        break;
      case 103 // NUM_7
      : if (((uiParam & SCAN_CODE_MASK) >> 16) == 71)
        { // Well its the NUM_0 key and not actually the INSERT key
          ubKey = 231;
        }
        else
        { // NOP, it's the INSERT key all right
          ubKey = usParam;
        }
        break;
      case 104 // NUM_8
      : if (((uiParam & SCAN_CODE_MASK) >> 16) == 72)
        { // Well its the NUM_PERIOD key and not actually the DELETE key
          ubKey = 232;
        }
        else
        { // NOP, it's the DELETE key all right
          ubKey = usParam;
        }
        break;
      case 105 // NUM_9
      : if (((uiParam & SCAN_CODE_MASK) >> 16) == 73)
        { // Well its the NUM_PERIOD key and not actually the DELETE key
          ubKey = 233;
        }
        else
        { // NOP, it's the DELETE key all right
          ubKey = usParam;
        }
        break;
      default
      : ubKey = usParam;
        break;
    }
  }
  else
  {
    if ((usParam >= 33)&&(usParam <= 46))
    { // Well this could be a NUMPAD character imitating the center console characters (when NUMLOCK is OFF). Well we
      // gotta find out what was pressed and translate it to the actual physical key (i.e. if we think that HOME was
      // pressed but NUM_7 was pressed, the we translate the key into NUM_7
      switch(usParam)
      { case 45 // NUM_0
        : if (((uiParam & SCAN_CODE_MASK) >> 16) == 82)
          { // Is it the NUM_0 key or the INSERT key
            if (((uiParam & EXT_CODE_MASK) >> 17) != 0)
            { // It's the INSERT key
              ubKey = 245;
            }
            else
            { // Is the NUM_0 key with NUM lock off
              ubKey = 234;
            }
          }
          else
          {
            ubKey = usParam;
          }
          break;
        case 46 // NUM_PERIOD
        : if (((uiParam & SCAN_CODE_MASK) >> 16) == 83)
          { // Is it the NUM_PERIOD key or the DEL key
            if (((uiParam & EXT_CODE_MASK) >> 17) != 0)
            { // It's the DELETE key
              ubKey = 246;
            }
            else
            { // Is the NUM_PERIOD key with NUM lock off
              ubKey = 235;
            }
          }
          else
          {
            ubKey = usParam;
          }
          break;
        case 35 // NUM_1
        : if (((uiParam & SCAN_CODE_MASK) >> 16) == 79)
          { // Is it the NUM_1 key or the END key
            if (((uiParam & EXT_CODE_MASK) >> 17) != 0)
            { // It's the END key
              ubKey = 247;
            }
            else
            { // Is the NUM_1 key with NUM lock off
              ubKey = 236;
            }
          }
          else
          {
            ubKey = usParam;
          }
          break;
        case 40 // NUM_2
        : if (((uiParam & SCAN_CODE_MASK) >> 16) == 80)
          { // Is it the NUM_2 key or the DOWN key
            if (((uiParam & EXT_CODE_MASK) >> 17) != 0)
            { // It's the DOWN key
              ubKey = 248;
            }
            else
            { // Is the NUM_2 key with NUM lock off
              ubKey = 237;
            }
          }
          else
          {
            ubKey = usParam;
          }
          break;
        case 34 // NUM_3
        : if (((uiParam & SCAN_CODE_MASK) >> 16) == 81)
          { // Is it the NUM_3 key or the PGDN key
            if (((uiParam & EXT_CODE_MASK) >> 17) != 0)
            { // It's the PGDN key
              ubKey = 249;
            }
            else
            { // Is the NUM_3 key with NUM lock off
              ubKey = 238;
            }
          }
          else
          {
            ubKey = usParam;
          }
          break;
        case 37 // NUM_4
        : if (((uiParam & SCAN_CODE_MASK) >> 16) == 75)
          { // Is it the NUM_4 key or the LEFT key
            if (((uiParam & EXT_CODE_MASK) >> 17) != 0)
            { // It's the LEFT key
              ubKey = 250;
            }
            else
            { // Is the NUM_4 key with NUM lock off
              ubKey = 239;
            }
          }
          else
          {
            ubKey = usParam;
          }
          break;
        case 39 // NUM_6
        : if (((uiParam & SCAN_CODE_MASK) >> 16) == 77)
          { // Is it the NUM_6 key or the RIGHT key
            if (((uiParam & EXT_CODE_MASK) >> 17) != 0)
            { // It's the RIGHT key
              ubKey = 251;
            }
            else
            { // Is the NUM_6 key with NUM lock off
              ubKey = 241;
            }
          }
          else
          {
            ubKey = usParam;
          }
          break;
        case 36 // NUM_7
        : if (((uiParam & SCAN_CODE_MASK) >> 16) == 71)
          { // Is it the NUM_7 key or the HOME key
            if (((uiParam & EXT_CODE_MASK) >> 17) != 0)
            { // It's the HOME key
              ubKey = 252;
            }
            else
            { // Is the NUM_7 key with NUM lock off
              ubKey = 242;
            }
          }
          else
          {
            ubKey = usParam;
          }
          break;
        case 38 // NUM_8
        : if (((uiParam & SCAN_CODE_MASK) >> 16) == 72)
          { // Is it the NUM_8 key or the UP key
            if (((uiParam & EXT_CODE_MASK) >> 17) != 0)
            { // It's the UP key
              ubKey = 253;
            }
            else
            { // Is the NUM_8 key with NUM lock off
              ubKey = 243;
            }
          }
          else
          {
            ubKey = usParam;
          }
          break;
        case 33 // NUM_9
        : if (((uiParam & SCAN_CODE_MASK) >> 16) == 73)
          { // Is it the NUM_9 key or the PGUP key
            if (((uiParam & EXT_CODE_MASK) >> 17) != 0)
            { // It's the PGUP key
              ubKey = 254;
            }
            else
            { // Is the NUM_9 key with NUM lock off
              ubKey = 244;
            }
          }
          else
          {
            ubKey = usParam;
          }
          break;
        default
        : ubKey = usParam;
          break;
      }
    }
    else
    {
      if (usParam == 12)
      { // NUM_5 with NUM_LOCK off
        ubKey = 240;
      }
      else
      { // Normal key
        ubKey = usParam;
      }
    }
  }

	// Find ucChar by translating ubKey using the gsKeyTranslationTable. If the SHIFT, ALT or CTRL key are down, then
  // the index into the translation table us changed from ubKey to ubKey+256, ubKey+512 and ubKey+768 respectively
  if (gfShiftState == TRUE)
  { // SHIFT is pressed, hence we add 256 to ubKey before translation to ubChar
    ubChar = gsKeyTranslationTable[ubKey+256];
  }
  else
  {
		//
		// Even though gfAltState is checked as if it was a BOOLEAN, it really contains 0x02, which
		// is NOT == to true.  This is broken, however to fix it would break Ja2 and Wizardry.
		// The same thing goes for gfCtrlState and gfShiftState, howver gfShiftState is assigned 0x01 which IS == to TRUE.
		// Just something i found, and thought u should know about.  DF.
		//

    if( gfAltState == TRUE )
    { // ALT is pressed, hence ubKey is multiplied by 3 before translation to ubChar
      ubChar = gsKeyTranslationTable[ubKey+512];
    }
    else
    {
      if (gfCtrlState == TRUE)
      { // CTRL is pressed, hence ubKey is multiplied by 4 before translation to ubChar
        ubChar = gsKeyTranslationTable[ubKey+768];
      }
      else
      { // None of the SHIFT, ALT or CTRL are pressed hence we have a default translation of ubKey
        ubChar = gsKeyTranslationTable[ubKey];
      }
    }
  }

  GetCursorPos(&MousePos);
  uiTmpLParam = ((MousePos.y << 16) & 0xffff0000) | (MousePos.x & 0x0000ffff);

  if (ufKeyState == TRUE)
  { // Key has been PRESSED
    // Find out if the key is already pressed and if not, queue an event and update the gfKeyState array
    if (gfKeyState[ubKey] == FALSE)
    { // Well the key has just been pressed, therefore we queue up and event and update the gsKeyState
      if (gfCurrentStringInputState == FALSE)
      {
        // There is no string input going on right now, so we queue up the event
        gfKeyState[ubKey] = TRUE;
        QueueEvent(KEY_DOWN, ubChar, uiTmpLParam);
			}
      else
      { // There is a current input string which will capture this event
        RedirectToString(ubChar);
        DbgMessage(TOPIC_INPUT, DBG_LEVEL_0, String("Pressed character %d (%d)", ubChar, ubKey));
      }
    }
    else
    { // Well the key gets repeated
      if (gfCurrentStringInputState == FALSE)
      { // There is no string input going on right now, so we queue up the event
        QueueEvent(KEY_REPEAT, ubChar, uiTmpLParam);
      }
      else
      { // There is a current input string which will capture this event
        RedirectToString(ubChar);
      }
    }
  }
  else
  { // Key has been RELEASED
    // Find out if the key is already pressed and if so, queue an event and update the gfKeyState array
    if (gfKeyState[ubKey] == TRUE)
    { // Well the key has just been pressed, therefore we queue up and event and update the gsKeyState
      gfKeyState[ubKey] = FALSE;
      QueueEvent(KEY_UP, ubChar, uiTmpLParam);
    }
		//else if the alt tab key was pressed
		else if( ubChar == TAB && gfAltState )
		{
			// therefore minimize the application
			ShowWindow( ghWindow, SW_MINIMIZE ); 
      gfKeyState[ ALT ] = FALSE;
			gfAltState = FALSE;
		}
  }
}

void KeyDown(UINT32 usParam, UINT32 uiParam)
{ // Are we PRESSING down one of SHIFT, ALT or CTRL ???
  if (usParam == 16)
  { // SHIFT key is PRESSED
    gfShiftState = SHIFT_DOWN;
    gfKeyState[16] = TRUE;
  }
  else
  {
    if (usParam == 17)
    { // CTRL key is PRESSED
      gfCtrlState = CTRL_DOWN;
      gfKeyState[17] = TRUE;
    }
    else
    {
      if (usParam == 18)
      { // ALT key is pressed
        gfAltState = ALT_DOWN;
        gfKeyState[18] = TRUE;
      }
      else
      {
        if (usParam == SNAPSHOT)
        {
          //PrintScreen();
					// DB Done in the KeyUp function
					// this used to be keyed to SCRL_LOCK
					// which I believe Luis gave the wrong value
        }
        else
        {
          // No special keys have been pressed
          // Call KeyChange() and pass TRUE to indicate key has been PRESSED and not RELEASED
          KeyChange(usParam, uiParam, TRUE);
        }
      }
    }
  }
}

void KeyUp(UINT32 usParam, UINT32 uiParam)
{ // Are we RELEASING one of SHIFT, ALT or CTRL ???
  if (usParam == 16)
  { // SHIFT key is RELEASED
    gfShiftState = FALSE;
    gfKeyState[16] = FALSE;
  }
  else
  {
    if (usParam == 17)
    { // CTRL key is RELEASED
      gfCtrlState = FALSE;
      gfKeyState[17] = FALSE;
    }
    else
    {
      if (usParam == 18)
      { // ALT key is RELEASED
        gfAltState = FALSE;
        gfKeyState[18] = FALSE;
      }
      else
      {
        if (usParam == SNAPSHOT)
        {
					// DB this used to be keyed to SCRL_LOCK
					// which I believe Luis gave the wrong value
					//#ifndef JA2
					if (_KeyDown(CTRL))
						VideoCaptureToggle();
					else
					//#endif
						PrintScreen();
        }
        else
        {
					// No special keys have been pressed
					// Call KeyChange() and pass FALSE to indicate key has been PRESSED and not RELEASED
					KeyChange(usParam, uiParam, FALSE);
        }
      }
    }
  }
}

void EnableDoubleClk(void)
{
	// Obsolete
}

void DisableDoubleClk(void)
{
	// Obsolete
}

void GetMousePos(SGPPoint *Point)
{
  POINT MousePos;

  GetCursorPos(&MousePos);

  Point->iX = (UINT32) MousePos.x;
  Point->iY = (UINT32) MousePos.y;

  return;
}

// These functions will be used for string input

// Since all string input will have to be handle by reentrant capable functions (since we must attend
// to windows messaging as well as network traffic related issues), whenever there is ongoing string input
// going on, we must use InitStringInput() and HandleStringInput() to get the job done. HandleStringInput()
// will return TRUE as long as the string input is going on, and FALSE when its done
//
// During string input, all keyboard are rerouted to the string and hence are not queued up on the
// event queue or registered in the state table. Also note that several string inputs can occur
// at the same time. Use the SetStringFocus() function to manager the focus for multiple
// string inputs

StringInput *InitStringInput(UINT16 *pInputString, UINT16 usLength, UINT16 *pFilter)
{
  StringInput *pStringDescriptor;

  if ((pStringDescriptor = MemAlloc(sizeof(StringInput))) == NULL)
  {
    //
    // Hum we failed to allocate memory for the string descriptor
    //

    DbgMessage(TOPIC_INPUT, DBG_LEVEL_1, "Failed to allocate memory for string descriptor");
    return NULL;
  }
  else
  {
    if ((pStringDescriptor->pOriginalString = MemAlloc(usLength * 2)) == NULL)
    {
      //
      // free up structure before aborting
      //

      MemFree(pStringDescriptor);
      DbgMessage(TOPIC_INPUT, DBG_LEVEL_1, "Failed to allocate memory for string duplicate");
      return NULL;
    }

    memcpy(pStringDescriptor->pOriginalString, pInputString, usLength * 2);

    pStringDescriptor->pString = pInputString;
    pStringDescriptor->pFilter = pFilter;
    pStringDescriptor->usMaxStringLength = usLength;
    pStringDescriptor->usStringOffset = 0;
    pStringDescriptor->usCurrentStringLength = 0;
    while ((pStringDescriptor->usStringOffset < pStringDescriptor->usMaxStringLength)&&(*(pStringDescriptor->pString + pStringDescriptor->usStringOffset) != 0))
    {
      //
      // Find the last character in the string
      //

      pStringDescriptor->usStringOffset++;
      pStringDescriptor->usCurrentStringLength++;
    }

    if (pStringDescriptor->usStringOffset == pStringDescriptor->usMaxStringLength)
    {
      //
      // Hum the current string has no null terminator. Invalidate the string and
      // start from scratch
      //

      memset(pStringDescriptor->pString, 0, usLength * 2);
      pStringDescriptor->usStringOffset = 0;
      pStringDescriptor->usCurrentStringLength = 0;
    }

    pStringDescriptor->fInsertMode = FALSE;
    pStringDescriptor->fFocus = FALSE;
    pStringDescriptor->pPreviousString = NULL;
    pStringDescriptor->pNextString = NULL;

    return pStringDescriptor;
  }
}

void    LinkPreviousString(StringInput *pCurrentString, StringInput *pPreviousString)
{
  if (pCurrentString != NULL)
  {
    if (pCurrentString->pPreviousString != NULL)
    {
      pCurrentString->pPreviousString->pNextString = NULL;
    }

    pCurrentString->pPreviousString = pPreviousString;

    if (pPreviousString != NULL)
    {
      pPreviousString->pNextString = pCurrentString;
    }
  }
}

void    LinkNextString(StringInput *pCurrentString, StringInput *pNextString)
{
  if (pCurrentString != NULL)
  {
    if (pCurrentString->pNextString != NULL)
    {
      pCurrentString->pNextString->pPreviousString = NULL;
    }

    pCurrentString->pNextString = pNextString;

    if (pNextString != NULL)
    {
      pNextString->pPreviousString = pCurrentString;
    }
  }
}

BOOLEAN CharacterIsValid(UINT16 usCharacter, UINT16 *pFilter)
{
  UINT32 uiIndex, uiEndIndex;

  if (pFilter != NULL)
  {
    uiEndIndex = *pFilter;
	  for (uiIndex = 1; uiIndex <= *pFilter; uiIndex++)
	  {
      if (usCharacter == *(pFilter + uiIndex))
      {
        return TRUE;
      }
	  }
    return FALSE;
  }

  return TRUE;
}

void    RedirectToString(UINT16 usInputCharacter)
{
  UINT16 usIndex;

  if (gpCurrentStringDescriptor != NULL)
  {
    // Handle the new character input
    switch (usInputCharacter)
    {
      case ENTER
      : // ENTER is pressed, the last character field should be set to ENTER
        if (gpCurrentStringDescriptor->pNextString != NULL)
        {
          gpCurrentStringDescriptor->fFocus = FALSE;
          gpCurrentStringDescriptor = gpCurrentStringDescriptor->pNextString;
          gpCurrentStringDescriptor->fFocus = TRUE;
          gpCurrentStringDescriptor->usLastCharacter = 0;
        }
        else
        {
          gpCurrentStringDescriptor->fFocus = FALSE;
          gpCurrentStringDescriptor->usLastCharacter = usInputCharacter;
          gfCurrentStringInputState = FALSE;
        }
        break;
      case ESC
      : // ESC was pressed, the last character field should be set to ESC
        gpCurrentStringDescriptor->fFocus = FALSE;
        gpCurrentStringDescriptor->usLastCharacter = usInputCharacter;
        gfCurrentStringInputState = FALSE;
        break;
      case SHIFT_TAB
      : // TAB was pressed, the last character field should be set to TAB
        if (gpCurrentStringDescriptor->pPreviousString != NULL)
        {
          gpCurrentStringDescriptor->fFocus = FALSE;
          gpCurrentStringDescriptor = gpCurrentStringDescriptor->pPreviousString;
          gpCurrentStringDescriptor->fFocus = TRUE;
          gpCurrentStringDescriptor->usLastCharacter = 0;
        }
        break;
      case TAB
      : // TAB was pressed, the last character field should be set to TAB
        if (gpCurrentStringDescriptor->pNextString != NULL)
        {
          gpCurrentStringDescriptor->fFocus = FALSE;
          gpCurrentStringDescriptor = gpCurrentStringDescriptor->pNextString;
          gpCurrentStringDescriptor->fFocus = TRUE;
          gpCurrentStringDescriptor->usLastCharacter = 0;
        }
        break;
      case UPARROW
      : // The UPARROW was pressed, the last character field should be set to UPARROW
        if (gpCurrentStringDescriptor->pPreviousString != NULL)
        {
          gpCurrentStringDescriptor->fFocus = FALSE;
          gpCurrentStringDescriptor = gpCurrentStringDescriptor->pPreviousString;
          gpCurrentStringDescriptor->fFocus = TRUE;
          gpCurrentStringDescriptor->usLastCharacter = 0;
        }
        break;
      case DNARROW
      : // The DNARROW was pressed, the last character field should be set to DNARROW
        if (gpCurrentStringDescriptor->pNextString != NULL)
        {
          gpCurrentStringDescriptor->fFocus = FALSE;
          gpCurrentStringDescriptor = gpCurrentStringDescriptor->pNextString;
          gpCurrentStringDescriptor->fFocus = TRUE;
          gpCurrentStringDescriptor->usLastCharacter = 0;
        }
        break;
      case LEFTARROW
      : // The LEFTARROW was pressed, move one character to the left
        if (gpCurrentStringDescriptor->usStringOffset > 0)
        { // Decrement the offset
          gpCurrentStringDescriptor->usStringOffset--;
        }
        gpCurrentStringDescriptor->usLastCharacter = usInputCharacter;
        break;
      case RIGHTARROW
      : // The RIGHTARROW was pressed, move one character to the right
        if (gpCurrentStringDescriptor->usStringOffset < gpCurrentStringDescriptor->usCurrentStringLength)
        { // Ok we can move the cursor one up without going past the end of string
          gpCurrentStringDescriptor->usStringOffset++;
        }
        gpCurrentStringDescriptor->usLastCharacter = usInputCharacter;
        break;
      case BACKSPACE
      : // Delete the character preceding the cursor
        if (gpCurrentStringDescriptor->usStringOffset > 0)
        { // Ok, we are not at the beginning of the string, so we may proceed
          for (usIndex = gpCurrentStringDescriptor->usStringOffset; usIndex <= gpCurrentStringDescriptor->usCurrentStringLength; usIndex++)
          { // Shift the characters one at a time
            *(gpCurrentStringDescriptor->pString + usIndex - 1) = *(gpCurrentStringDescriptor->pString + usIndex);
          }
          gpCurrentStringDescriptor->usStringOffset--;
          gpCurrentStringDescriptor->usCurrentStringLength--;
        }

        break;
      case DEL
      : // Delete the character which follows the cursor
        if (gpCurrentStringDescriptor->usStringOffset < gpCurrentStringDescriptor->usCurrentStringLength)
        { // Ok we are not at the end of the string, so we may proceed
          for (usIndex = gpCurrentStringDescriptor->usStringOffset; usIndex < gpCurrentStringDescriptor->usCurrentStringLength; usIndex++)
          { // Shift the characters one at a time
            *(gpCurrentStringDescriptor->pString + usIndex) = *(gpCurrentStringDescriptor->pString + usIndex + 1);
          }
          gpCurrentStringDescriptor->usCurrentStringLength--;
        }
        gpCurrentStringDescriptor->usLastCharacter = usInputCharacter;
        break;
      case INSERT
      : // Toggle insert mode
        if (gpCurrentStringDescriptor->fInsertMode == TRUE)
        {
          gpCurrentStringDescriptor->fInsertMode = FALSE;
        }
        else
        {
          gpCurrentStringDescriptor->fInsertMode = TRUE;
        }
        gpCurrentStringDescriptor->usLastCharacter = usInputCharacter;
        break;
      case HOME
      : // Go to the beginning of the input string
        gpCurrentStringDescriptor->usStringOffset = 0 ;
        gpCurrentStringDescriptor->usLastCharacter = usInputCharacter;
        break;
#ifndef JA2
// Stupid definition causes problems with headers that use the keyword END -- DB
		case KEY_END
#else
		case END
#endif
      : // Go to the end of the input string
        gpCurrentStringDescriptor->usStringOffset = gpCurrentStringDescriptor->usCurrentStringLength;
        gpCurrentStringDescriptor->usLastCharacter = usInputCharacter;
        break;
      default
      : //
        // normal input
        //
        if (CharacterIsValid(usInputCharacter, gpCurrentStringDescriptor->pFilter) == TRUE)
        {
          if (gpCurrentStringDescriptor->fInsertMode == TRUE)
          { // Before we can shift characters for the insert, we must make sure we have the space
            if (gpCurrentStringDescriptor->usCurrentStringLength < (gpCurrentStringDescriptor->usMaxStringLength - 1))
            { // Before we can add a new character we must shift existing ones to for the insert
              for (usIndex = gpCurrentStringDescriptor->usCurrentStringLength; usIndex > gpCurrentStringDescriptor->usStringOffset; usIndex--)
              { // Shift the characters one at a time
                *(gpCurrentStringDescriptor->pString + usIndex) = *(gpCurrentStringDescriptor->pString + usIndex - 1);
              }
              // Ok now we introduce the new character
              *(gpCurrentStringDescriptor->pString + usIndex) = usInputCharacter;
              gpCurrentStringDescriptor->usStringOffset++;
              gpCurrentStringDescriptor->usCurrentStringLength++;
            }
          }
          else
          {
            // Ok, add character to string (by overwriting)
            if (gpCurrentStringDescriptor->usStringOffset < (gpCurrentStringDescriptor->usMaxStringLength - 1))
            { // Ok, we have not exceeded the maximum number of characters yet
              *(gpCurrentStringDescriptor->pString + gpCurrentStringDescriptor->usStringOffset) = usInputCharacter;
              gpCurrentStringDescriptor->usStringOffset++;
            }
            // Did we push back the current string length (i.e. add character to end of string)
            if (gpCurrentStringDescriptor->usStringOffset > gpCurrentStringDescriptor->usCurrentStringLength)
            { // Add a NULL character
              *(gpCurrentStringDescriptor->pString + gpCurrentStringDescriptor->usStringOffset) = 0;
              gpCurrentStringDescriptor->usCurrentStringLength++;
            }
          }
          gpCurrentStringDescriptor->usLastCharacter = usInputCharacter;
        }
        break;
    }
  }
}

UINT16 GetStringInputState(void)
{
  if (gpCurrentStringDescriptor != NULL)
  {
    return gpCurrentStringDescriptor->usLastCharacter;
  }
  else
  {
    return 0;
  }
}

BOOLEAN StringInputHasFocus(void)
{
  return gfCurrentStringInputState;
}

BOOLEAN SetStringFocus(StringInput *pStringDescriptor)
{
  if (pStringDescriptor != NULL)
  {
    if (gpCurrentStringDescriptor != NULL)
    {
      gpCurrentStringDescriptor->fFocus = FALSE;
    }
    // Ok overide current entry
    gfCurrentStringInputState = TRUE;
    gpCurrentStringDescriptor = pStringDescriptor;
    gpCurrentStringDescriptor->fFocus = TRUE;
    gpCurrentStringDescriptor->usLastCharacter = 0;
    return TRUE;
  }
  else
  {
    if (gpCurrentStringDescriptor != NULL)
    {
      gpCurrentStringDescriptor->fFocus = FALSE;
    }
    // Ok overide current entry
    gfCurrentStringInputState = FALSE;
    gpCurrentStringDescriptor = NULL;
    return TRUE;
  }
}

UINT16 GetCursorPositionInString(StringInput *pStringDescriptor)
{
  return pStringDescriptor->usStringOffset;
}

BOOLEAN StringHasFocus(StringInput *pStringDescriptor)
{
  if (pStringDescriptor != NULL)
  {
    return pStringDescriptor->fFocus;
  }
  else
  {
    return FALSE;
  }
}

void RestoreString(StringInput *pStringDescriptor)
{
  memcpy(pStringDescriptor->pString, pStringDescriptor->pOriginalString, pStringDescriptor->usMaxStringLength * 2);

  pStringDescriptor->usStringOffset = 0;
  pStringDescriptor->usCurrentStringLength = 0;
  while ((pStringDescriptor->usStringOffset < pStringDescriptor->usMaxStringLength)&&(*(pStringDescriptor->pString + pStringDescriptor->usStringOffset) != 0))
  {
    //
    // Find the last character in the string
    //

    pStringDescriptor->usStringOffset++;
    pStringDescriptor->usCurrentStringLength++;
  }

  if (pStringDescriptor->usStringOffset == pStringDescriptor->usMaxStringLength)
  {
    //
    // Hum the current string has no null terminator. Invalidate the string and
    // start from scratch
    //
    memset(pStringDescriptor->pString, 0, pStringDescriptor->usMaxStringLength * 2);
    pStringDescriptor->usStringOffset = 0;
    pStringDescriptor->usCurrentStringLength = 0;
  }

  pStringDescriptor->fInsertMode = FALSE;
}

void EndStringInput(StringInput *pStringDescriptor)
{ // Make sure we have a valid pStringDescriptor
  if (pStringDescriptor != NULL)
  { // make sure the gpCurrentStringDescriptor is NULL if necessary
    if (pStringDescriptor == gpCurrentStringDescriptor)
    {
      gpCurrentStringDescriptor = NULL;
      gfCurrentStringInputState = FALSE;
    }
    // Make sure we have a valid string within the string descriptor
    if (pStringDescriptor->pOriginalString != NULL)
    { // free up the string
      MemFree(pStringDescriptor->pOriginalString);
    }
    // free up the descriptor
    MemFree(pStringDescriptor);
  }
}



//
// Miscellaneous input-related utility functions:
//

void RestrictMouseToXYXY(UINT16 usX1, UINT16 usY1, UINT16 usX2, UINT16 usY2)
{
	SGPRect TempRect;

	TempRect.iLeft   = usX1;
	TempRect.iTop    = usY1;
	TempRect.iRight  = usX2;
	TempRect.iBottom = usY2;

	RestrictMouseCursor(&TempRect);
}

//SB
void SBCenterRestrictMouseCursor(SGPRect *pRectangle)
{
  // Make a copy of our rect....
  memcpy( &gCursorClipRect, pRectangle, sizeof( gCursorClipRect ) );
  gCursorClipRect.left += (SCREEN_WIDTH-640)/2;
  gCursorClipRect.right += (SCREEN_WIDTH-640)/2;
  gCursorClipRect.top += (SCREEN_HEIGHT-480)/2;
  gCursorClipRect.bottom += (SCREEN_HEIGHT-480)/2;
//  ClipCursor((RECT *)pRectangle);
  ClipCursor((RECT *)&gCursorClipRect);
	fCursorWasClipped = TRUE;
}

void RestrictMouseCursor(SGPRect *pRectangle)
{
  // Make a copy of our rect....
  memcpy( &gCursorClipRect, pRectangle, sizeof( gCursorClipRect ) );
  ClipCursor((RECT *)pRectangle);
	fCursorWasClipped = TRUE;
}

void FreeMouseCursor(void)
{
  ClipCursor(NULL);
	fCursorWasClipped = FALSE;
}

void RestoreCursorClipRect( void )
{
  if ( fCursorWasClipped )
  {
    ClipCursor( &gCursorClipRect );
  }
}

void GetRestrictedClipCursor( SGPRect *pRectangle )
{
	GetClipCursor((RECT *) pRectangle );
}

BOOLEAN IsCursorRestricted( void )
{
	return( fCursorWasClipped );
}

void SimulateMouseMovement( UINT32 uiNewXPos, UINT32 uiNewYPos )
{
	FLOAT flNewXPos, flNewYPos;

	// Wizardry NOTE: This function currently doesn't quite work right for in any Windows resolution other than SCREEN_WIDTHxSCREEN_HEIGHT.
	// mouse_event() uses your current Windows resolution to calculate the resulting x,y coordinates.  So in order to get
	// the right coordinates, you'd have to find out the current Windows resolution through a system call, and then do:
	//		uiNewXPos = uiNewXPos * SCREEN_WIDTH  / WinScreenResX;
	//		uiNewYPos = uiNewYPos * SCREEN_HEIGHT / WinScreenResY;
	//
	// JA2 doesn't have this problem, 'cause they use DirectDraw calls that change the Windows resolution properly.
	//
	// Alex Meduna, Dec. 3, 1997

	// Adjust coords based on our resolution
	flNewXPos = ( (FLOAT)uiNewXPos / SCREEN_WIDTH ) * 65536;
	flNewYPos = ( (FLOAT)uiNewYPos / SCREEN_HEIGHT ) * 65536;

	mouse_event( MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, (UINT32)flNewXPos, (UINT32)flNewYPos, 0, 0 );
}



BOOLEAN InputEventInside(InputAtom *Event, UINT32 uiX1, UINT32 uiY1, UINT32 uiX2, UINT32 uiY2)
{
  UINT32 uiEventX, uiEventY;

  uiEventX = _EvMouseX(Event);
  uiEventY = _EvMouseY(Event);

  return((uiEventX >= uiX1) && (uiEventX <= uiX2) && (uiEventY >= uiY1) && (uiEventY <= uiY2));
}


void DequeueAllKeyBoardEvents()
{
	InputAtom  InputEvent;
	MSG				 KeyMessage;


	//dequeue all the events waiting in the windows queue
	while( PeekMessage( &KeyMessage, ghWindow, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE ) );

	//Deque all the events waiting in the SGP queue
	while (DequeueEvent(&InputEvent) == TRUE)
  {
		//dont do anything
	}
}



void HandleSingleClicksAndButtonRepeats( void )
{
  UINT32 uiTimer;


  uiTimer = GetTickCount();

  // Is there a LEFT mouse button repeat
  if (gfLeftButtonState)
  {
    if ((guiLeftButtonRepeatTimer > 0)&&(guiLeftButtonRepeatTimer <= uiTimer))
    {
      UINT32 uiTmpLParam;
      POINT  MousePos;

      GetCursorPos(&MousePos);
      uiTmpLParam = ((MousePos.y << 16) & 0xffff0000) | (MousePos.x & 0x0000ffff);
      QueueEvent(LEFT_BUTTON_REPEAT, 0, uiTmpLParam);
      guiLeftButtonRepeatTimer = uiTimer + BUTTON_REPEAT_TIME;
    }
  }
  else
  {
    guiLeftButtonRepeatTimer = 0;
  }


	// Is there a RIGHT mouse button repeat
	if (gfRightButtonState)
  {
    if ((guiRightButtonRepeatTimer > 0)&&(guiRightButtonRepeatTimer <= uiTimer))
    {
      UINT32 uiTmpLParam;
      POINT  MousePos;

      GetCursorPos(&MousePos);
      uiTmpLParam = ((MousePos.y << 16) & 0xffff0000) | (MousePos.x & 0x0000ffff);
      QueueEvent(RIGHT_BUTTON_REPEAT, 0, uiTmpLParam);
      guiRightButtonRepeatTimer = uiTimer + BUTTON_REPEAT_TIME;
    }
  }
  else
  {
    guiRightButtonRepeatTimer = 0;
  }
}


INT16 GetMouseWheelDeltaValue( UINT32 wParam )
{
	INT16 sDelta = HIWORD( wParam );

	return( sDelta / WHEEL_DELTA );
}
